package com.onesports.intelligent.k12.polarlight.common.errors.translators;

import cn.gjing.excel.base.exception.ExcelAssertException;
import cn.gjing.excel.base.exception.ExcelTemplateException;
import cn.oneframework.common.utils.MessageUtils;
import cn.oneframework.common.utils.StringUtils;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.onesports.framework.kit.common.valid.ParamValidException;
import com.onesports.intelligent.k12.polarlight.common.errors.exceptions.BusinessException;
import com.onesports.intelligent.k12.polarlight.common.errors.exceptions.UnLoginException;
import com.onesports.intelligent.k12.polarlight.common.utils.Result;
import com.onesports.intelligent.k12.polarlight.common.utils.ResultUtil;
import com.onesports.intelligent.k12.polarlight.domain.vo.common.FieldErrorVO;
import com.onesports.intelligent.k12.polarlight.security.OrganizationNotActivatedException;
import com.onesports.intelligent.k12.polarlight.security.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.context.NoSuchMessageException;
import org.springframework.dao.DataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.NativeWebRequest;
import org.zalando.problem.Problem;
import org.zalando.problem.Status;
import org.zalando.problem.spring.web.advice.ProblemHandling;
import org.zalando.problem.spring.web.advice.security.SecurityAdviceTrait;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author onesports
 */
@RestControllerAdvice
//@Profile(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT)
@Slf4j
public class ExceptionTranslator implements ProblemHandling, SecurityAdviceTrait {
    @Override
    public ResponseEntity process(@Nullable ResponseEntity<Problem> entity, NativeWebRequest request) {
        if (entity == null) {
            return ResultUtil.error(MessageUtils.message("system.exception"));
        }
        Problem problem = entity.getBody();
        if (Status.FORBIDDEN == problem.getStatus()) {
            return unauthorizedError(problem.getDetail());
        }
        return ResultUtil.error(problem.getDetail());
    }

    @Override
    public ResponseEntity handleAuthentication(final AuthenticationException e, final NativeWebRequest request) {
        String msg = "";
        if (e instanceof OrganizationNotActivatedException) {
            msg = e.getMessage();
        }
        return unauthorizedError(msg);
    }

    /**
     * 拦截业务exception返回消息
     */
    @ExceptionHandler(value = BusinessException.class)
    public ResponseEntity handleBusinessExceptionHandler(BusinessException e) {
        return ResultUtil.error(e.getMessage(), e.getErrData());
    }

    /**
     * 拦截数据访问异常处理
     *
     * @param e e
     * @return {@link ResponseEntity}
     */
    @ExceptionHandler(DataAccessException.class)
    @ResponseBody
    public ResponseEntity dataAccessExceptionHandler(DataAccessException e) {
        log.error("服务器内部错误：数据库操作异常{}", e);
        return ResultUtil.error(String.valueOf(HttpStatus.INTERNAL_SERVER_ERROR.value()), "出错啦...请稍后重试或联系管理员");
    }

    /**
     * Excel断言异常
     *
     * @param e ExcelAssertException
     * @return ErrorResult
     */
    @ExceptionHandler(ExcelAssertException.class)
    public ResponseEntity ede(ExcelAssertException e) {
        return ResultUtil.businessError("第" + (e.getRowIndex() + 1) + "行第" + (e.getColIndex() + 1) + "列" + e.getMessage() + ",导入失败");
    }

    /**
     * 模板匹配异常
     *
     * @param e ExcelTemplateException
     * @return ErrorResult
     */
    @ExceptionHandler(ExcelTemplateException.class)
    public ResponseEntity ee(ExcelTemplateException e) {
        return ResultUtil.businessError("Excel模板不匹配");
    }

    /**
     * 拦截MybatisPlus异常处理
     *
     * @param request 请求
     * @param e       e
     * @return {@link ResponseEntity}
     */
    @ExceptionHandler(MybatisPlusException.class)
    public ResponseEntity mybatisPlusExceptionHandler(HttpServletRequest request, MybatisPlusException e) {
        log.error("非法参数异常{}", e);
        return ResultUtil.error(String.valueOf(HttpStatus.INTERNAL_SERVER_ERROR.value()), e.getMessage());
    }

    @ExceptionHandler(ParamValidException.class)
    public ResponseEntity handleParamValidExceptionHandler(ParamValidException e) {
        return ResultUtil.error(e.getMessage());
    }


    @ExceptionHandler(UsernameNotFoundException.class)
    public ResponseEntity userNameNotFoundExceptionHandler(UsernameNotFoundException e) {
        return ResultUtil.businessError(e.getMessage());
    }


    @ExceptionHandler(NoSuchMessageException.class)
    public ResponseEntity handleNoSuchMessageExceptionHandler(NoSuchMessageException e) {
        return ResultUtil.businessError(e.getMessage());
    }

    /**
     * 拦截业务exception返回消息
     */
    @ExceptionHandler(InternalAuthenticationServiceException.class)
    public ResponseEntity handleInternalAuthenticationServiceExceptionHandler(InternalAuthenticationServiceException e) {
        return ResultUtil.error(e.getMessage());
    }

    @ExceptionHandler(UnLoginException.class)
    public ResponseEntity handleUnLoginExceptionHandler(UnLoginException e) {
        return ResultUtil.unauthorizedError(e.getMessage());
    }


    @ExceptionHandler(WxPayException.class)
    public ResponseEntity wxPay(WxPayException e) {
        log.error("微信支付异常：{}", e.getMessage());
        return ResultUtil.error("微信支付异常...请稍后重试或联系管理员");
    }

    @ExceptionHandler(WxErrorException.class)
    public ResponseEntity wxError(WxErrorException e) {
        log.error("微信接口异常：{}", e.getMessage());
        return ResultUtil.error("微信接口异常...请稍后重试或联系管理员");
    }

    /**
     * Exception 基础类
     */
    @ExceptionHandler(InvalidFormatException.class)
    public ResponseEntity ie(InvalidFormatException ex) {
        return ResultUtil.businessError("参数异常");
    }

    /**
     * 拦截DTO校验错误，同时返回详细字段消息
     */
    @Override
    public ResponseEntity handleMethodArgumentNotValid(MethodArgumentNotValidException ex, @Nonnull NativeWebRequest request) {
        log.warn("请求参数校验", ex);
        BindingResult result = ex.getBindingResult();
        List<String> fieldErrors = result.getFieldErrors().stream()
                .map(f -> new FieldErrorVO(f.getObjectName().replaceFirst("DTO$", ""), f.getField(), f.getDefaultMessage()))
                .collect(Collectors.toList()).stream().map(FieldErrorVO::getMessage).collect(Collectors.toList());

        String fieldMsg = String.join(",", fieldErrors);
        return ResultUtil.error(fieldMsg);
    }


    /**
     * Exception 基础类
     */
    @ExceptionHandler(Exception.class)
    public ResponseEntity exception(Exception ex) {
        log.error("出错啦...{}", ex);
        return ResultUtil.internalServerError("出错啦...请稍后重试或联系管理员");
    }

    private ResponseEntity unauthorizedError(String msg) {
        Result result = new Result();
        msg = StringUtils.isEmpty(msg) ? "登录已过期，请重新登录！" : msg;
        if (!SecurityUtils.isAuthenticated()) {
            result.setCode(Status.UNAUTHORIZED.getStatusCode());
            result.setMsg(msg);
            return new ResponseEntity(result, HttpStatus.UNAUTHORIZED);
        } else {
            result.setCode(Status.FORBIDDEN.getStatusCode());
            result.setMsg(msg);
            return new ResponseEntity(result, HttpStatus.OK);
        }
    }


}
